<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />

  <script>

    // 一个为对象添加 Iterator 接口的例子 

    // let obj = {
    //   data: ["aa", "bb", "cc"],
    //   [Symbol.iterator]() {
    //     let self = this.data;
    //     let index = 0;
    //     return {
    //       next: () => {
    //         if (index <= self.length - 1) {
    //           return { done: false, value: self[index++] };

    //         } else {
    //           return { done: true, value: undefined };
    //         }
    //       }
    //     }
    //   }
    // }

    // for (let item of obj) {
    //   console.log(item);

    // }
    // let arr = [...obj];
    // console.log(arr);  


    // 一个为对象添加 Iterator 接口的例子 

    // class MyClass {

    //   value = "";
    //   nextNode = null;

    //   constructor(value) {
    //     this.value = value
    //   }
    //   [Symbol.iterator]() {
    //     let currentNode = this;
    //     // let iterator = { next: next };
    //     return {
    //       next: function aa() {
    //         if (currentNode != null) {
    //           let value = currentNode.value;
    //           currentNode = currentNode.nextNode;
    //           return { done: false, value: value }
    //         } else {
    //           return { done: true, value: undefined };
    //         }
    //       }
    //     };
    //     // return iterator;
    //   }
    // }
    // const obj = new MyClass("1");
    // const obj2 = new MyClass("2");
    // const obj3 = new MyClass("3");

    // obj.nextNode = obj2;
    // obj2.nextNode = obj3;
    // for (let item of obj) {
    //   console.log(item);
    // }


    // 一个为对象添加 Iterator 接口的例子 end

    // // 遍历器实现“链表”结构的例子 start
    // function Obj(value) {
    //   this.value = value;
    //   this.nextNode = null;
    //   this[Symbol.iterator] = function () {
    //     var iterator = { next: next };
    //     var current = this;

    //     function next() {
    //       if (current) {
    //         var value = current.value;
    //         current = current.nextNode;
    //         return { done: false, value: value };
    //       }
    //       return { done: true };
    //     }
    //     return iterator;
    //   }
    // }
    // var one = new Obj(1);
    // var two = new Obj(2);
    // var three = new Obj(3);
    // one.nextNode = two;
    // two.nextNode = three;

    // for (var i of one) {
    //   console.log(i); // 1, 2, 3
    // }
    // for (var i of two) {
    //   console.log(i); //  2, 3
    // }
    // for (var i of three) {
    //   console.log(i); //  3
    // }

    // // Obj.prototype[Symbol.iterator] = function () {
    // //     var iterator = { next: next };
    // //     var current = this;

    // //     function next() {
    // //         if (current) {
    // //             var value = current.value;
    // //             current = current.nextNode;
    // //             return { done: false, value: value };
    // //         }
    // //         return { done: true };
    // //     }
    // //     return iterator;
    // // }

    // // one:{value:1,nextNode:two},two:{value:2,nextNode:two},three:{value:3,nextNode:null}

    // var one = new Obj(1);
    // var two = new Obj(2);
    // var three = new Obj(3);
    // one.nextNode = two;
    // two.nextNode = three;

    // Set 和 Map 结构
    // Set 和 Map 结构也原生具有 Iterator 接口，可以直接使用for...of循环。

    // 计算生成的数据结构
    // 有些数据结构是在现有数据结构的基础上，计算生成的。比如，ES6 的数组、Set、Map 都部署了以下三个方法，调用后都返回遍历器对象。

    // entries() 返回一个遍历器对象，用来遍历[键名, 键值]组成的数组。对于数组，键名就是索引值；对于 Set，键名与键值相同。Map 结构的 Iterator 接口，默认就是调用entries方法。
    // keys() 返回一个遍历器对象，用来遍历所有的键名。
    // values() 返回一个遍历器对象，用来遍历所有的键值。


    //     并不是所有类似数组的对象都具有 Iterator 接口，一个简便的解决方法，就是使用Array.from方法将其转为数组。

    // let arrayLike = { length: 2, 0: 'a', 1: 'b' };

    // // 报错
    // for (let x of arrayLike) {
    //   console.log(x);
    // }

    // // 正确
    // for (let x of Array.from(arrayLike)) {
    //   console.log(x);
    // }


    // 对象
    // 对于普通的对象，for...of结构不能直接使用，会报错，必须部署了 Iterator 接口后才能使用。
    // 但是，这样情况下，for...in循环依然可以用来遍历键名。
    // let obj = { 0: "1", 1: "2" };

    // for (let item in obj) {

    //   console.log(item);
    // }

    // Reflect.defineProperty(obj, "length", { value: 2, writable: true, enumerable: true, configurable: true });


    // for (let item of Array.from(obj)) {

    //   console.log(item)

    // }

    // 使用Object.keys方法将对象的键名生成一个数组，然后遍历这个数组。

    let obj = { p1: "1", p2: "2", p3: "3", [Symbol("p4")]: "Symbol4" };
    Reflect.defineProperty(obj, "p5", { value: '5', writable: true, enumerable: false, configurable: true });

    // for (let key of Reflect.ownKeys(obj)) {
    //   // **Reflect.ownKeys(obj)和Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))**等价
    //   console.log(key);
    // }
    // for(let key of Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))){
    //   console.log(key);
    // }

    // for (let key of Object.keys(obj)) {
    //   // **Object.keys(obj)**返回遍历对象可枚举类型的属性，不包括继承的属性
    //   console.log(key); // 不返回"p5",[Symbol("p4")] 
    // }

    // 另一个方法是使用 Generator 函数将对象重新包装一下。

    // function* myGenerator(obj) {
    //   for (let item of Reflect.ownKeys(obj)) {
    //     yield obj[item];
    //   }

    // }

    // let myarray = myGenerator(obj);

    // for (let item of myarray) {
    //   console.log(item);
    // }

    // 与其他遍历语法的比较
    // 以数组为例，JavaScript 提供多种遍历语法。最原始的写法就是for循环。

    // for (var index = 0; index < myArray.length; index++) {
    // console.log(myArray[index]);
    // }
    // 这种写法比较麻烦，因此数组提供内置的forEach方法。

    // myArray.forEach(function (value) {
    // console.log(value);
    // });
    // 这种写法的问题在于，无法中途跳出forEach循环，break命令或return命令都不能奏效。

    // for...in循环可以遍历数组的键名。

    // for (var index in myArray) {
    // console.log(myArray[index]);
    // }
    // for...in循环有几个缺点。

    // 数组的键名是数字，但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
    // for...in循环不仅遍历数字键名，还会遍历手动添加的其他键，甚至包括原型链上的键。
    // 某些情况下，for...in循环会以任意顺序遍历键名。
    // 总之，for...in循环主要是为遍历对象而设计的，不适用于遍历数组。

    // for...of循环相比上面几种做法，有一些显著的优点。

    // for (let value of myArray) {
    // console.log(value);
    // }
    // 有着同for...in一样的简洁语法，但是没有for...in那些缺点。
    // 不同于forEach方法，它可以与break、continue和return配合使用。
    // 提供了遍历所有数据结构的统一操作接口。
    // 下面是一个使用 break 语句，跳出for...of循环的例子。

    // for (var n of fibonacci) {
    // if (n > 1000)
    // break;
    // console.log(n);
    // }
    // 上面的例子，会输出斐波纳契数列小于等于 1000 的项。如果当前项大于 1000，就会使用break语句跳出for...of循环。
    

  </script>
</head>

<body>

</body>

</html>